---
title: Events
description: Learn how to listen to Embla Carousel events and how to make use of them.
order: 2
date: 2021-02-21
---

import { Tabs } from 'components/Tabs/Tabs'
import { TabsItem } from 'components/Tabs/TabsItem'
import { TABS_LIBRARY } from 'consts/tabs'

# Events

Embla Carousel exposes **events** that you can listen to in order to **react** to **changes** in the carousel.

---

## Usage

You need an **initialized carousel** in order to **make use of events**. Events will only be fired during the lifecycle of a carousel and added event listeners will persist even when you hard reset the carousel with the [reInit](/api/methods/#reinit) method.

### Adding event listeners

After initializing a carousel, we're going to **subscribe** to the [slidesInView](/api/events/#slidesinview) **event** in the following example:

<Tabs groupId={TABS_LIBRARY.GROUP_ID}>
<TabsItem tab={TABS_LIBRARY.TABS.VANILLA}>

```js highlight={10}
import EmblaCarousel from 'embla-carousel'

const emblaNode = document.querySelector('.embla')
const emblaApi = EmblaCarousel(emblaNode)

function logSlidesInView(emblaApi) {
  console.log(emblaApi.slidesInView())
}

emblaApi.on('slidesInView', logSlidesInView)
```

</TabsItem>
<TabsItem tab={TABS_LIBRARY.TABS.REACT}>

```jsx highlight={12}
import { useCallback, useEffect } from 'react'
import useEmblaCarousel from 'embla-carousel-react'

export function EmblaCarousel() {
  const [emblaRef, emblaApi] = useEmblaCarousel()

  const logSlidesInView = useCallback((emblaApi) => {
    console.log(emblaApi.slidesInView())
  }, [])

  useEffect(() => {
    if (emblaApi) emblaApi.on('slidesInView', logSlidesInView)
  }, [emblaApi, logSlidesInView])

  // ...
}
```

</TabsItem>
<TabsItem tab={TABS_LIBRARY.TABS.VUE}>

```html highlight={12}
<script setup>
  import { onMounted } from 'vue'
  import emblaCarouselVue from 'embla-carousel-vue'

  const [emblaRef, emblaApi] = emblaCarouselVue()

  function logSlidesInView(emblaApi) {
    console.log(emblaApi.slidesInView())
  }

  onMounted(() => {
    if (emblaApi.value) emblaApi.value.on('slidesInView', logSlidesInView)
  })

  // ...
</script>
```

</TabsItem>
<TabsItem tab={TABS_LIBRARY.TABS.SOLID}>

```jsx highlight={13}
import { onMount } from 'solid-js'
import createEmblaCarousel from 'embla-carousel-solid'

export function EmblaCarousel() {
  const [emblaRef, emblaApi] = createEmblaCarousel()

  function logSlidesInView(emblaApi) {
    console.log(emblaApi.slidesInView())
  }

  onMount(() => {
    const api = emblaApi()
    if (api) api.on('slidesInView', logSlidesInView)
  })

  // ...
}
```

</TabsItem>
<TabsItem tab={TABS_LIBRARY.TABS.SVELTE}>

```html highlight={12}
<script>
  import emblaCarouselSvelte from 'embla-carousel-svelte'

  let emblaApi

  function logSlidesInView(emblaApi) {
    console.log(emblaApi.slidesInView())
  }

  function onInit(event) {
    emblaApi = event.detail
    emblaApi.on('slidesInView', logSlidesInView)
  }
</script>

<div class="embla" use:emblaCarouselSvelte onemblaInit="{onInit}">...</div>
```

<Admonition type="note">
  **Note:** Starting with Svelte 5, the `on:` event handlers have been deprecated. However, `on:emblaInit` will remain for backward compatibility.
</Admonition>

</TabsItem>
</Tabs>

### Removing event listeners

In order to remove an event listener, you'll have to call the [off](/api/methods/#off) method and make sure to pass the **same callback reference** you passed to the [on](/api/methods/#off) method:

<Tabs groupId={TABS_LIBRARY.GROUP_ID}>
<TabsItem tab={TABS_LIBRARY.TABS.VANILLA}>

```js highlight={8}
import EmblaCarousel from 'embla-carousel'

const emblaNode = document.querySelector('.embla')
const emblaApi = EmblaCarousel(emblaNode)

function logSlidesInViewOnce(emblaApi) {
  console.log(emblaApi.slidesInView())
  emblaApi.off('slidesInView', logSlidesInViewOnce)
}

emblaApi.on('slidesInView', logSlidesInViewOnce)
```

</TabsItem>
<TabsItem tab={TABS_LIBRARY.TABS.REACT}>

```jsx highlight={9}
import { useCallback, useEffect } from 'react'
import useEmblaCarousel from 'embla-carousel-react'

export function EmblaCarousel() {
  const [emblaRef, emblaApi] = useEmblaCarousel()

  const logSlidesInViewOnce = useCallback((emblaApi) => {
    console.log(emblaApi.slidesInView())
    emblaApi.off('slidesInView', logSlidesInViewOnce)
  }, [])

  useEffect(() => {
    if (emblaApi) emblaApi.on('slidesInView', logSlidesInViewOnce)
  }, [emblaApi, logSlidesInViewOnce])

  // ...
}
```

</TabsItem>
<TabsItem tab={TABS_LIBRARY.TABS.VUE}>

```html highlight={9}
<script setup>
  import { onMounted } from 'vue'
  import emblaCarouselVue from 'embla-carousel-vue'

  const [emblaRef, emblaApi] = emblaCarouselVue()

  function logSlidesInViewOnce(emblaApi) {
    console.log(emblaApi.slidesInView())
    emblaApi.off('slidesInView', logSlidesInViewOnce)
  }

  onMounted(() => {
    if (emblaApi.value) emblaApi.value.on('slidesInView', logSlidesInViewOnce)
  })

  // ...
</script>
```

</TabsItem>
<TabsItem tab={TABS_LIBRARY.TABS.SOLID}>

```jsx highlight={9}
import { onMount } from 'solid-js'
import createEmblaCarousel from 'embla-carousel-solid'

export function EmblaCarousel() {
  const [emblaRef, emblaApi] = createEmblaCarousel()

  function logSlidesInViewOnce(emblaApi) {
    console.log(emblaApi.slidesInView())
    emblaApi.off('slidesInView', logSlidesInViewOnce)
  }

  onMount(() => {
    const api = emblaApi()
    if (api) api.on('slidesInView', logSlidesInViewOnce)
  })

  // ...
}
```

</TabsItem>
<TabsItem tab={TABS_LIBRARY.TABS.SVELTE}>

```html highlight={8}
<script>
  import emblaCarouselSvelte from 'embla-carousel-svelte'

  let emblaApi

  function logSlidesInViewOnce(emblaApi) {
    console.log(emblaApi.slidesInView())
    emblaApi.off('slidesInView', logSlidesInViewOnce)
  }

  function onInit(event) {
    emblaApi = event.detail
    emblaApi.on('slidesInView', logSlidesInViewOnce)
  }
</script>

<div class="embla" use:emblaCarouselSvelte onemblaInit="{onInit}">...</div>
```

<Admonition type="note">
  **Note:** Starting with Svelte 5, the `on:` event handlers have been deprecated. However, `on:emblaInit` will remain for backward compatibility.
</Admonition>

</TabsItem>
</Tabs>

### TypeScript

The `EmblaEventType` is obtained directly from the **core package** `embla-carousel` and used like so:

<Tabs groupId={TABS_LIBRARY.GROUP_ID}>
<TabsItem tab={TABS_LIBRARY.TABS.VANILLA}>

```ts asLanguage=tsx highlight={3,11}
import EmblaCarousel, {
  EmblaCarouselType,
  EmblaEventType
} from 'embla-carousel'

const emblaNode = document.querySelector('.embla')
const emblaApi = EmblaCarousel(emblaNode)

function logEmblaEvent(
  emblaApi: EmblaCarouselType,
  eventName: EmblaEventType
): void {
  console.log(`Embla just triggered ${eventName}!`)
}

emblaApi.on('slidesInView', logEmblaEvent)
```

</TabsItem>
<TabsItem tab={TABS_LIBRARY.TABS.REACT}>

```tsx highlight={2,9}
import React, { useCallback } from 'react'
import { EmblaCarouselType, EmblaEventType } from 'embla-carousel'
import useEmblaCarousel from 'embla-carousel-react'

export function EmblaCarousel() {
  const [emblaRef, emblaApi] = useEmblaCarousel()

  const logEmblaEvent = useCallback(
    (emblaApi: EmblaCarouselType, eventName: EmblaEventType) => {
      console.log(`Embla just triggered ${eventName}!`)
    },
    []
  )

  useEffect(() => {
    if (emblaApi) emblaApi.on('slidesInView', logEmblaEvent)
  }, [emblaApi, logEmblaEvent])

  // ...
}
```

<Admonition type="warning">
  If you're using `pnpm`, you need to install `embla-carousel` as a
  **devDependency** when importing types from it like demonstrated above.
  <br />
  This is because even though `embla-carousel-react` has `embla-carousel` as a
  dependency, `pnpm` makes nested dependencies inaccessible by design.
</Admonition>

</TabsItem>
<TabsItem tab={TABS_LIBRARY.TABS.VUE}>

```html highlight={3,10}
<script setup>
  import { onMounted } from 'vue'
  import { EmblaCarouselType, EmblaEventType } from 'embla-carousel'
  import emblaCarouselVue from 'embla-carousel-vue'

  const [emblaRef] = emblaCarouselVue()

  function logEmblaEvent(
    emblaApi: EmblaCarouselType,
    eventName: EmblaEventType
  ): void {
    console.log(`Embla just triggered ${eventName}!`)
  }

  onMounted(() => {
    if (emblaApi.value) emblaApi.value.on('slidesInView', logEmblaEvent)
  })

  // ...
</script>
```

<Admonition type="warning">
  If you're using `pnpm`, you need to install `embla-carousel` as a
  **devDependency** when importing types from it like demonstrated above.
  <br />
  This is because even though `embla-carousel-vue` has `embla-carousel` as a
  dependency, `pnpm` makes nested dependencies inaccessible by design.
</Admonition>

</TabsItem>
<TabsItem tab={TABS_LIBRARY.TABS.SOLID}>

```jsx highlight={2,10}
import { onMount } from 'solid-js'
import { EmblaCarouselType, EmblaEventType } from 'embla-carousel'
import createEmblaCarousel from 'embla-carousel-solid'

export function EmblaCarousel() {
  const [emblaRef, emblaApi] = createEmblaCarousel()

  function logEmblaEvent(
    emblaApi: EmblaCarouselType,
    eventName: EmblaEventType
  ): void {
    console.log(`Embla just triggered ${eventName}!`)
  }

  onMount(() => {
    const api = emblaApi()
    if (api) api.on('slidesInView', logEmblaEvent)
  })

  // ...
}
```

<Admonition type="warning">
  If you're using `pnpm`, you need to install `embla-carousel` as a
  **devDependency** when importing types from it like demonstrated above.
  <br />
  This is because even though `embla-carousel-solid` has `embla-carousel` as a
  dependency, `pnpm` makes nested dependencies inaccessible by design.
</Admonition>

</TabsItem>
<TabsItem tab={TABS_LIBRARY.TABS.SVELTE}>

```html highlight={2,9}
<script>
  import { EmblaCarouselType, EmblaEventType } from 'embla-carousel'
  import emblaCarouselSvelte from 'embla-carousel-svelte'

  let emblaApi: EmblaCarouselType

  function logEmblaEvent(
    emblaApi: EmblaCarouselType,
    eventName: EmblaEventType
  ): void {
    console.log(`Embla just triggered ${eventName}!`)
  }

  function onInit(event: CustomEvent<EmblaCarouselType>): void {
    emblaApi = event.detail
    emblaApi.on('slidesInView', logEmblaEvent)
  }
</script>

<div class="embla" use:emblaCarouselSvelte onemblaInit="{onInit}">...</div>
```

<Admonition type="note">
  **Note:** Starting with Svelte 5, the `on:` event handlers have been deprecated. However, `on:emblaInit` will remain for backward compatibility.
</Admonition>

<Admonition type="warning">
  If you're using `pnpm`, you need to install `embla-carousel` as a
  **devDependency** when importing types from it like demonstrated above.
  <br />
  This is because even though `embla-carousel-svelte` has `embla-carousel` as a
  dependency, `pnpm` makes nested dependencies inaccessible by design.
</Admonition>

</TabsItem>
</Tabs>

## Reference

Below follows an exhaustive **list of all** Embla Carousel **events** together with information about how they work.

---

### init

Once: <BrandPrimaryText>`yes`</BrandPrimaryText>

Runs when the carousel mounts for the first time. This only fires once which means that it won't fire when the carousel is re-initialized using the [reInit](/api/methods/#reinit) method.

---

### reInit

Once: <BrandPrimaryText>`no`</BrandPrimaryText>

Runs when the [reInit](/api/methods/#reinit) method is called. When the window is resized, Embla Carousel automatically calls the [reInit](/api/methods/#reinit) method which will also fire this event.

---

### destroy

Once: <BrandPrimaryText>`yes`</BrandPrimaryText>

Runs when the carousel has been destroyed using the [destroy](/api/methods/#destroy) method. This only fires once and will be the last event the carousel fires.

---

### select

Once: <BrandPrimaryText>`no`</BrandPrimaryText>

Runs when the selected scroll snap changes. The select event is triggered by drag interactions or the [scrollNext](/api/methods/#scrollnext), [scrollPrev](/api/methods/#scrollPrev) or [scrollTo](/api/methods/#scrollto) methods.

---

### scroll

Once: <BrandPrimaryText>`no`</BrandPrimaryText>

Runs when the carousel is scrolling. It might be a good idea to throttle this if you're doing expensive stuff in your callback function.

---

### settle

Once: <BrandPrimaryText>`no`</BrandPrimaryText>

Runs when the carousel has settled after scroll has been triggered. Please note that this can take longer than you think when [dragFree](/api/options/#dragfree) is enabled or when using slow [transitions](/api/options/#duration).

---

### resize

Once: <BrandPrimaryText>`no`</BrandPrimaryText>

Runs when the carousel container or the slide sizes change. It's using [ResizeObserver](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver) under the hood.

---

### slidesInView

Once: <BrandPrimaryText>`no`</BrandPrimaryText>

Runs when any slide has **entered** or **exited** the viewport. This event is intended to be used together with the [slidesInView](/api/methods/#slidesinview) and/or [slidesNotInView](/api/methods/#slidesnotinview) methods.

---

### slidesChanged

Once: <BrandPrimaryText>`no`</BrandPrimaryText>

Runs when slides are added to, or removed from the carousel [container](/api/options/#container). It's using [MutationObserver](https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver) under the hood.

---

### slideFocus

Once: <BrandPrimaryText>`no`</BrandPrimaryText>

Runs when a slide receives focus. For example, when a focusable element like a button, link or input receives focus inside a slide.

---

### pointerDown

Once: <BrandPrimaryText>`no`</BrandPrimaryText>

Runs when the user has a pointer down on the carousel. It's triggered by a `touchstart` or a `mousedown` event.

---

### pointerUp

Once: <BrandPrimaryText>`no`</BrandPrimaryText>

Runs when the user has released the pointer from the carousel. It's triggered by a `touchend` or a `mouseup` event.

---
